package com.gitee.wsl.math.vector

import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sqrt


interface Vector :List<Double>{

    //val size: Int

    val argmax: Int

    val numActives: Int

    val numNonzeros: Int

    fun toSparseWithSize(nnz: Int): SparseVector

    fun toArray(): DoubleArray

    fun copy(): SparseVector

    operator fun set(index: Int,value:Double)

    fun foreachActive(f: (Int, Double) -> Unit)

    operator fun times(scalar: Float): Vector

    operator fun div(f: Float): Vector

    operator fun times(vector: Vector): Double

    operator fun plus(vector: Vector): Vector

    operator fun minus(vector: Vector): Vector

    fun norm(): Double

    companion object{
        /**
         * 返回两个向量之间的平方距离。
         * @param v1 第一个向量。
         * @param v2 第二个向量。
         * @return 两个向量之间的平方距离。
         */
        fun sqdist(v1: Vector, v2: Vector): Double {
            require(v1.size == v2.size) { "向量维度不匹配：Dim(v1)=${v1.size}，Dim(v2)=${v2.size}。" }
            var squaredDistance = 0.0
            when{
               v1 is SparseVector &&  v2 is SparseVector -> {
                   val v1Values = v1.values
                   val v1Indices = v1.indices
                   val v2Values = v2.values
                   val v2Indices = v2.indices
                   val nnzv1 = v1Indices.size
                   val nnzv2 = v2Indices.size

                   var kv1 = 0
                   var kv2 = 0
                   while (kv1 < nnzv1 || kv2 < nnzv2) {
                       var score = 0.0

                       if (kv2 >= nnzv2 || (kv1 < nnzv1 && v1Indices[kv1] < v2Indices[kv2])) {
                           score = v1Values[kv1]
                           kv1 += 1
                       } else if (kv1 >= nnzv1 || (kv2 < nnzv2 && v2Indices[kv2] < v1Indices[kv1])) {
                           score = v2Values[kv2]
                           kv2 += 1
                       } else {
                           score = v1Values[kv1] - v2Values[kv2]
                           kv1 += 1
                           kv2 += 1
                       }
                       squaredDistance += score * score
                   }
               }
               /* case (v1: SparseVector, v2: DenseVector) =>
                squaredDistance = sqdist(v1, v2)

                case (v1: DenseVector, v2: SparseVector) =>
                squaredDistance = sqdist(v2, v1)

                case (DenseVector(vv1), DenseVector(vv2)) =>
                var kv = 0
                val sz = vv1.length
                while (kv < sz) {
                    val score = vv1(kv) - vv2(kv)
                    squaredDistance += score * score
                    kv += 1
                }*/
                else -> throw  IllegalArgumentException("不支持的向量类型：$v1 和 $v2")
            }
            return squaredDistance
        }
    }

}

/*fun Vector.asFlow()= flow{
    foreachActive{key,value->
        //emit(key to value)
    }
}*/

/**
 * 返回该向量的p-范数。
 * @param vector 输入向量。
 * @param p 范数。
 * @return L^p^空间中的范数。
 */
fun Vector.norm(p: Double): Double {
    require(p >= 1.0) { "为了计算向量的p-范数，要求指定p>=1。您指定的p=$p。" }

    val values = when(this) {
        is SparseVector -> this.values
        else  -> throw  IllegalArgumentException("不支持的向量类型：$this" )
    }
    val size = values.size

    return when (p) {
        1.0 -> {
            var sum = 0.0
            var i = 0
            while (i < size) {
                sum += abs(values[i])
                i += 1
            }
            sum
        }
        2.0 -> {
            var sum = 0.0
            var i = 0
            while (i < size) {
                sum += values[i] * values[i]
                i += 1
            }
            sqrt(sum)
        }
        Double.POSITIVE_INFINITY -> {
            var max = 0.0
            var i = 0
            while (i < size) {
                val value = abs(values[i])
                if (value > max) max = value
                i += 1
            }
            max
        }
        else -> {
            var sum = 0.0
            var i = 0
            while (i < size) {
                sum += abs(values[i]).pow(p)
                i += 1
            }
            sum.pow(1.0 / p)
        }
    }
}
